import java.util.ArrayList;

/** Link Class -- a point-to-point link
    CS158A Spring 2010 Project #2
    @author Clare Lanam & Loc Phan
    */

public class Link
{
   /** Cost to traverse this link */
   private int cost;
   
   /** Port on one end */
   private Port portA;
   /** Port on the other end */
   private Port portB;
   
   // ------[CONSTRUCTOR]------ //
   
   public Link(int cost) { this.cost = cost; }
   
   // ------[PUBLIC METHODS]------ //
   
   /** Adds a port to this link, if possible.
       @param p the port to add to this link
       @return false if attachment failed
       */
   
   public boolean attach(Port p)
   {
      if (p == null || p == portA || p == portB)
         return false;
      
      if (portA == null)
         portA = p;
      else if (portB == null)
         portB = p;
      else return false;
      
      if (p.getLink() != this)
         p.setLink(this);
      
      return true;
   }
   
   /** Returns the path cost of this link.
       @return the path cost of this link.
       */
   
   public int getPathCost() { return cost; }
   
   /** Gets the bridge connected to port p by this link.
       @param p the near port
       @return the bridge connected to port p by this link
       */
   
   public Bridge getDistalBridge(Port p)
   {
      Port distalPort = getDistalPort(p);
      if (distalPort != null)
         return distalPort.getBridge();
      return null;
   }
   
   /** Gets the port connected to port p by this link.
       @param p the near port
       @return the port connected to port p by this link
       */
   
   public Port getDistalPort(Port p)
   {
      if (portA == p) return portB;
      if (portB == p) return portA;
      return null;
   }
   
   /** Returns the port on this link that has the lowest
       cost path to the root bridge.
       @return the port with the best BPDUs on this link
       */
   
   public Port getLowestCostPort()
   {
      if (portA == null)
         return portB;
      if (portB == null)
         return portA;
      
      if (portA.getBridge().getStoredRootBridgeID().compareTo(
            portB.getBridge().getStoredRootBridgeID()) < 0)
         return portA;
      else if (portA.getBridge().getStoredRootBridgeID().compareTo(
            portB.getBridge().getStoredRootBridgeID()) > 0)
         return portB;
      
      if (portA.getBridge().getPathCostToRoot()
            < portB.getBridge().getPathCostToRoot())
         return portA;
      if (portA.getBridge().getPathCostToRoot()
            > portB.getBridge().getPathCostToRoot())
         return portB;
      
      // tie breaker:
      if (portA.getBridge().getID().compareTo(portB.getBridge().getID()) < 0)
         return portA;
      return portB;
   }
   
   /** Returns an array list of all connected ports.
       @return an array list of all connected ports
       */
   
   public ArrayList<Port> getPorts()
   {
      ArrayList<Port> allPorts = new ArrayList<Port>();
      
      if (portA != null) allPorts.add(portA);
      if (portB != null) allPorts.add(portB);
      
      return allPorts;
   }
   
   /** Returns whether this link is active.  Link is active
       if at least two ports are forwarding.
       @return whether this link is active
       */
   
   public boolean isActive()
   {
      if (portA == null || portB == null)
         return false;
      return (portA.getState() == PortState.FORWARDING
            && portB.getState() == PortState.FORWARDING);
   }
   
   /** Removes a port from this link. */
   
   public void removePort(Port p)
   {
      if (portA == p || portB == p)
         p.setLink(null);
      if (portA == p) portA = null;
      if (portB == p) portB = null;
   }
   
   /** Remove all ports from this link. */
   
   public void removeAllPorts()
   {
      removePort(portA);
      removePort(portB);
   }
   
   /** Sends a BPDU along this link. */
   
   public void send(Port source, BPDU bpdu)
   {
      if (portA != null && portA != source)
         portA.receive(bpdu);
      if (portB != null && portB != source)
         portB.receive(bpdu);
   }
}
